#pragma once

#ifndef _CORE_XML_CONNECTION_H_
#define _CORE_XML_CONNECTION_H_

#include "Poco\Runnable.h"
#include "Poco\SharedPtr.h"
#include "Poco\Mutex.h"
#include "Poco\BasicEvent.h"
#include "Poco\Event.h"
#include "Poco\Net\StreamSocket.h"
#include "Poco\SAX\InputSource.h"
#include "Poco\SAX\SAXParser.h"
#include "Poco\DOM\Document.h"
#include "Poco\SAX\WhitespaceFilter.h"

#include "Core\StanzaHandler.h"
#include "Core\SafeSocketStream.h"
#include "Core\SafeMutex.h"

namespace ExamSystem
{
    using Poco::Runnable;
    using Poco::SharedPtr;
    using Poco::BasicEvent;
    using Poco::Event;
    using Poco::XML::InputSource;
    using Poco::XML::SAXParser;
    using Poco::XML::WhitespaceFilter;
    using Poco::XML::Document;
    using Poco::Net::StreamSocket;
    using Poco::Net::SocketStream;
    using Poco::Net::SocketInputStream;

    class CExamServerConnectionManager;
    class CCoreXMLHandler;
    class CCoreXMLHandlerManager;


    /**************************************************************************\
     Component:     CXMLConnection
     Description:   
        This class manages a connected sockets. 
        1. It holds a xml parser running on a certain thread.
        2. It provides a interface for sending keep-alive package.
        3. It can automatically reply a keep-alive package.
    \**************************************************************************/
    class CXMLConnection : public Runnable, public CStanzaHandler
    {
    public:
        CXMLConnection( 
            const StreamSocket& s,
            CCoreXMLHandlerManager *xmlManager,
            bool flagServerConnection = true
            );
        ~CXMLConnection(void);

    public:
        // from Runnable
        virtual void run(void);

        // from CStanzaHandler
        virtual void startElement(CXMLConnection *connection, const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(CXMLConnection *connection, const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(CXMLConnection *connection, const XMLChar ch[], int start, int length);

    public:
        // Send a keep-alive package to the client
        void SendKeepAlivePackage(void);

        // Check if the last keep-alive package has been replied
        bool IsKeepAlivePackageReplied(void);

        // check if the stream is started
        bool IsStreamStarted(void);

        // send a XML stanza
        bool SendXMLStanza(Document &xmlStanza);

        // Send raw bytes 
        bool SendRawByte(const void *data, int length);

        // send start-stream label
        void StartStream(void);
        // Send end-stream label and stop the stream
        // the caller part will only send the label
        // the receiver part will close the socket
        void EndStream(void);

        // Stop the connection forcefully
        // this function send end-stream label and close the socket immediately 
        void CloseConnection(void);

        // local ID, it is actually the index managed by the connection manager
        int GetLocalID(void) const;
        void SetLocalID(int localID);

        // get xml handler
        SharedPtr<CCoreXMLHandler> &GetXMLHandler(void);

        // get the stream
        SharedPtr<SafeSocketOutputStream> &GetSocketOutputStream(void);

        // wait until the connection stopped
        void WaitForConnectionStop(void);

        // get internal socket
        StreamSocket &GetSocket(void);

    public:
        // handlers for events
        void OnInvalidStream(const void* pSender, int &);
        void OnStreamStart(const void* pSender, int&);
        void OnStreamEnd(const void* pSender, int&);

    public:
        // events
        BasicEvent<int> eventConnectionClosed;

        // syn objects
        Event synStreamStart;

    protected:
        // flag that identifies whether this is a server connection
        // this flag will be set to true by default
        bool m_flagServerConnection;

        // the local Id for this connection
        int m_localID;

        // connected socket
        StreamSocket m_socket;

        // stream and SAXParser
        SharedPtr<InputSource> m_pInputSource;
        SharedPtr<SocketInputStream> m_pISocketStream;
        SharedPtr<SafeSocketOutputStream> m_pOSocketStream;
        SharedPtr<CCoreXMLHandler> m_pXMLHandler;
        SharedPtr<SAXParser> m_pParser;
        SharedPtr<WhitespaceFilter> m_pWhitespaceFilter;

        // flag that identifies that a keep-alive package has been sent
        bool m_flagKeepAlivePackageSent;

        // flag that identifies that the stream start
        bool m_flagStreamStart;
        // whether we have sent the start label
        bool m_flagStreamStartLabelSent;
        bool m_flagStreamEndLabelSent;

        // lock for flags
        CSafeMutex m_lockStreamStartFlag;
        CSafeMutex m_lockKeepaliveFlag;

        // lock for running
        CSafeMutex m_runningLock;

        // package type, for handler
        std::string m_packageType;

        // the connection has been closed forcefully
        CSafeMutex m_forceClosedFlagLock;
        bool m_flagForceClosed;
    };
}


#endif